package org.liu.simpleTank;

import java.util.ArrayList;
import java.util.EventListener;
import java.util.EventObject;
import java.util.Iterator;

public class Tank extends MovableElement implements Hittable {
	/*
	 * range from 0 to 5
	 * 0 : die
	 * 1 : normal ( only 1 bullet per time allowed )
	 * 2 : fast bullet
	 * 3 : fast bullet with count limit of 2
	 * 4 : steel destroyable fast bullet with count limit of 2
	 * 5 : grass destroyable
	 */
	private int life;
	private ControlStatus status;
	private double speed;
	public static final double SPEED_NORMAL = 0.005;
	public static final double SPEED_FAST = 0.008;
	{
		life = 1;
		status = new ControlStatus();
		speed = SPEED_NORMAL;
	}
	
	public ControlStatus getStatus() {
		return status;
	}
	
	public void setSpeed(double speed) {
		this.speed = speed;
	}
		
	public Tank(int type) {
		super(type, 2, 2);
	}
	
	@Override
	public void update(UpdateContext ctx) {
		// fire related
		if(status.getFire())
			fire();
		// move related
		updateMove(ctx);
	}

	public void updateMove(UpdateContext ctx) {
		if(isMoving()) {
			int i = addProgress(ctx.getDelta() * speed);
			if(i > 0) {
				rmOpposite(direction);
				tryNewMove();
			}
		} else
			tryNewMove();
	}
	
	private void tryNewMove() {
		if(status.getDir() == CONSTS.NODIR) {
			// stop moving
			setNotMoving();
			resetProgress();
			endMove(); // emit move ended event
			return;
		} else { // try continue moving
			direction = status.getDir();
			if(canMove()) {
				if(!isMoving()) // not moving before
					beginMove(); // emit move began event
				setMoving();
				expand(direction);
			} else { // blocked by something
				setNotMoving();
				resetProgress();
				blocked(); // emit blocked event          
			}
		}
	}
	
	private ArrayList<Integer> conflicts;
	{
		conflicts = new ArrayList<Integer>();
		conflicts.add(CONSTS.BRICK);
		conflicts.add(CONSTS.STEEL);
		conflicts.add(CONSTS.WATER);
		conflicts.add(CONSTS.HOME);
		conflicts.add(CONSTS.TANK_1);
		conflicts.add(CONSTS.TANK_2);
		conflicts.add(CONSTS.TANK_3);
		conflicts.add(CONSTS.TANK_PLAYER1);
		conflicts.add(CONSTS.TANK_PLAYER2);
	}
	
	private boolean canMove() {
		Cell[] cells = cellOn(direction);
		if(cells == null)
			return false;
		Iterator<Element> iter = Cell.cellsGet(cells).iterator();
		
		while(iter.hasNext()) {
			if(conflicts.contains(iter.next().type))
				return false;
		}
		return true;
	}
	
	// tank events
	public static abstract class TankListener implements EventListener {
		public void beginMove(EventObject e) { }
		public void endMove(EventObject e) { }
		public void blocked(EventObject e) { }
		public void fire(EventObject e) { }
	}
	
	private ArrayList<TankListener> listeners;
	{
		listeners = new ArrayList<Tank.TankListener>(4);
	}
	
	public void addListener(TankListener listener) {
		listeners.add(listener);
	}
	
	private EventObject e;
	{ // shared EventObject to save object creation time & memory 
		e = new EventObject(this);
	}
	
	private void beginMove() {
		Iterator<TankListener> iter = listeners.iterator();
		while(iter.hasNext()) {
			iter.next().beginMove(e);
		}
	}
	
	private void endMove() {
		Iterator<TankListener> iter = listeners.iterator();
		while(iter.hasNext()) {
			iter.next().endMove(e);
		}
	}
	
	private void blocked() {
		Iterator<TankListener> iter = listeners.iterator();
		while(iter.hasNext()) {
			iter.next().blocked(e);
		}
	}
	
	private void fire() {
		Iterator<TankListener> iter = listeners.iterator();
		while(iter.hasNext()) {
			iter.next().fire(e);
		}
	}
	
	// remove listeners after destruction
	@Override
	public void destroy() {
		super.destroy();
		listeners.clear();
	}
	
	// Hittable interface implementation
	@Override
	public int getLife() {
		return life;
	}
	@Override
	public void setLife(int life) {
		if(life > 0 && life <= 5)
			this.life = life; 
	}
	@Override
	public int incLife() { if(life < 5) ++life; return getLife(); }
	@Override
	public void hit() {
		if(--life == 0)
			destroy();
	}
	
}
